package galaxy

import (
	"context"
	"fmt"
	"reflect"

	"github.com/facebookgo/inject"
	"github.com/facebookgo/structtag"
)

type ServerProvider struct {
	Name     string
	Config   interface{} `galaxy:"required"`
	Inject   interface{}
	OnCreate func() error                    // 初始化提供对象，注册服务后执行
	OnStart  func(ctx context.Context) error // 执行该服务对象，在所有对象注册后执行
	OnStop   func() error                    // 停止服务
}

type serverProvideWrap struct {
	ServerProvider

	object *inject.Object
}

var (
	serverGraph inject.Graph
	servers     []*serverProvideWrap
	serverName  map[string]int
	inited      bool
)

func init() {
	serverGraph = inject.Graph{}
	servers = make([]*serverProvideWrap, 0)
	serverName = make(map[string]int)
	inited = false
}

func registerProvide(provide interface{}, name string) (*inject.Object, error) {
	o := &inject.Object{Value: provide, Name: name}
	err := serverGraph.Provide(o)
	return o, err
}

func RegisterInterface(in interface{}) error {
	return serverGraph.Provide(&inject.Object{Value: in, Complete: true})
}

func Populate(in interface{}) error {
	return serverGraph.Provide(&inject.Object{Value: in})
}

func registerInject(inject interface{}) error {
	v := reflect.ValueOf(inject).Elem()
	t := reflect.TypeOf(inject).Elem()
	for k := 0; k < t.NumField(); k++ {
		f := t.Field(k)
		found, value, err := structtag.Extract("galaxy", string(f.Tag))
		if err != nil {
			return err
		}
		if found {
			field := v.Field(k)
			var fieldInject bool
			var errorIgnore bool
			if value == "inject" {
				fieldInject = true
				errorIgnore = false
			} else if value == "ignore" {
				fieldInject = true
				errorIgnore = true
			} else {
				fieldInject = false
			}
			if fieldInject {
				if err := RegisterInterface(field.Interface()); !errorIgnore && err != nil {
					return err
				}
			}

		}
	}
	return nil
}

func InjectInterface(out interface{}) error {
	v := reflect.ValueOf(out).Elem()
	_, err := registerProvide(out, v.String())
	if err != nil {
		return err
	}
	if err := serverGraph.Populate(); err != nil {
		return err
	}
	if err := registerInject(out); err != nil {
		return err
	}
	return nil
}

func RegisterConfig(name string, conf interface{}, required bool) error {
	if err := config.Bind(name, conf, required); err != nil {
		return err
	}
	logger.Infof("[ Server ] config inject:%#v -> %#v", name, conf)
	return RegisterInterface(conf)
}

func RegisterServer(provider ServerProvider) error {
	if inited {
		return fmt.Errorf("Server is running, do not register server")
	}

	_, ok := serverName[provider.Name]
	if ok {
		return fmt.Errorf("Server(%s) is exists, need checked again", provider.Name)
	}
	wrap := &serverProvideWrap{
		ServerProvider: provider,
	}

	if provider.Config != nil {
		f, _ := reflect.TypeOf(provider).FieldByName("Config")
		found, value, err := structtag.Extract("galaxy", string(f.Tag))
		if err != nil {
			return err
		}
		required := false
		if found {
			if value == "option" {
				required = false
			} else if value == "required" {
				required = true
			}
		} else {
			required = true
		}
		err = RegisterConfig(wrap.Name, wrap.Config, required)
		if err != nil {
			return err
		}
	}

	servers = append(servers, wrap)
	serverName[provider.Name] = len(servers)
	return nil
}

func bindServer() error {
	if inited {
		return fmt.Errorf("Server is inited")
	}
	serverGraph.Logger = logger
	l := len(servers)
	for index := 0; index < l; index++ {
		wrap := servers[index]

		//for k := 0; k < t.NumField(); k++ {
		//	//f := wrap.t.Field(k)
		//	ff := v.Field(k)
		//	// 对于已经实例化的，inject其内部元素
		//	if !ff.IsNil() {
		//		Populate(ff.Interface())
		//	}
		//}
		if wrap.Inject != nil {
			object, err := registerProvide(wrap.Inject, wrap.Name)
			if err != nil {
				return err
			}
			if err := serverGraph.Populate(); err != nil {
				return err
			}
			wrap.object = object
		}
		if wrap.OnCreate != nil {
			logger.Infof("[ Server ] bind %#v -> onCreate", wrap.Name)
			if err := wrap.OnCreate(); err != nil {
				return err
			}
		}
		if wrap.Inject != nil {
			if err := registerInject(wrap.Inject); err != nil {
				return err
			}
			wrap.object.Complete = true
		}

	}
	inited = true
	return nil
}

func startServer(ctx context.Context) error {
	if !inited {
		return fmt.Errorf("Server is not init, do not start")
	}
	l := len(servers) - 1
	for index := l; index >= 0; index-- {
		wrap := servers[index]
		if wrap.OnStart != nil {
			logger.Infof("[ Server ] start %#v -> onStart", wrap.Name)
			if err := wrap.OnStart(ctx); err != nil {
				return err
			}
		}
	}
	return nil
}

func stopServer() error {
	l := len(servers) - 1
	for index := l; index >= 0; index-- {
		wrap := servers[index]
		if wrap.OnStop != nil {
			logger.Infof("[ Server ] stop %#v -> onStop", wrap.Name)
			if err := wrap.OnStop(); err != nil {
				return err
			}
		}
	}
	return nil
}
